package com.hsjk.szwj.module.member.service.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.annotations.VisibleForTesting;
import com.hsjk.szwj.framework.common.enums.CommonStatusEnum;
import com.hsjk.szwj.framework.common.pojo.PageResult;
import com.hsjk.szwj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hsjk.szwj.framework.security.core.util.SecurityFrameworkUtils;
import com.hsjk.szwj.framework.tenant.core.aop.TenantIgnore;
import com.hsjk.szwj.framework.tenant.core.context.TenantContextHolder;
import com.hsjk.szwj.module.infra.api.file.FileApi;
import com.hsjk.szwj.module.infra.dal.dataobject.config.ConfigDO;
import com.hsjk.szwj.module.infra.dal.mysql.config.ConfigMapper;
import com.hsjk.szwj.module.member.api.user.dto.MemberCreateReqDTO;
import com.hsjk.szwj.module.member.api.user.dto.MemberUserRespDTO;
import com.hsjk.szwj.module.member.controller.admin.user.vo.AdminMemberDetailInviterMemberRespVO;
import com.hsjk.szwj.module.member.controller.admin.user.vo.AdminMemberDetailRespVO;
import com.hsjk.szwj.module.member.controller.admin.user.vo.AdminMemberPageReqVO;
import com.hsjk.szwj.module.member.controller.admin.user.vo.AdminMemberRespVO;
import com.hsjk.szwj.module.member.controller.app.user.vo.AppUpdateMemberReqVO;
import com.hsjk.szwj.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import com.hsjk.szwj.module.member.convert.user.UserConvert;
import com.hsjk.szwj.module.member.dal.dataobject.user.MemberUserDO;
import com.hsjk.szwj.module.member.dal.mysql.user.MemberUserMapper;
import com.hsjk.szwj.module.member.enums.MemberNewStatusEnum;
import com.hsjk.szwj.module.system.api.sms.SmsCodeApi;
import com.hsjk.szwj.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import com.hsjk.szwj.module.system.enums.sms.SmsSceneEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static com.hsjk.szwj.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.hsjk.szwj.framework.common.util.servlet.ServletUtils.getClientIP;
import static com.hsjk.szwj.module.member.enums.ErrorCodeConstants.USER_NOT_EXISTS;

/**
 * 会员 User Service 实现类
 */
@Service
@Valid
@Slf4j
public class MemberUserServiceImpl implements MemberUserService {

    @Resource
    private MemberUserMapper memberUserMapper;

    @Resource
    private ConfigMapper configMapper;

    @Resource
    private FileApi fileApi;
    @Resource
    private SmsCodeApi smsCodeApi;

    @Resource
    private PasswordEncoder passwordEncoder;


    @Override
    public MemberUserDO getUserByMobile(String mobile) {
        return memberUserMapper.selectByMobile(mobile);
    }

    @Override
    public List<MemberUserDO> getUserListByNickname(String nickname) {
        return memberUserMapper.selectListByNicknameLike(nickname);
    }

    @Override
    public MemberUserDO createUserIfAbsent(String mobile, String registerIp) {
        // 用户已经存在
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user != null) {
            return user;
        }
        // 用户不存在，则进行创建
        return this.createUser(mobile, registerIp);
    }

    @Override
    public MemberUserRespDTO createMemberIfAbsent(MemberCreateReqDTO reqDTO) {
        MemberUserDO memberUserDO = memberUserMapper.selectByUnionId(reqDTO.getUnionId());
        MemberUserRespDTO memberUserRespDTO = null;
        if (memberUserDO != null) {
            // 有邀请人
            if(Objects.nonNull(reqDTO.getInviterMemberId())){
                // 用户本省不存在 邀请人
                if (Objects.isNull(memberUserDO.getInviterMemberId())) {
                    MemberUserDO updateInvite = new MemberUserDO();
                    updateInvite.setId(memberUserDO.getId());
                    updateInvite.setInviterMemberId(reqDTO.getInviterMemberId());
                    updateInvite.setInviterTime(LocalDateTime.now());
                    memberUserMapper.updateById(updateInvite);
                    memberUserDO.setInviterMemberId(reqDTO.getInviterMemberId());
                }else{
                    LocalDateTime inviteDate = memberUserDO.getInviterTime();
                    // 存在邀请人 | 是否再锁定期限内
                    ConfigDO configDO = configMapper.selectByKey("share.locked");
                    if(Objects.nonNull(configDO) && StringUtils.isNotEmpty(configDO.getValue()) && Objects.nonNull(inviteDate)){
                        LocalDateTime nowDate = LocalDateTime.now();
                        // 锁定期限
                        long lockedDay =  Long.parseLong( configDO.getValue());
                        // 计算相差的天数
                        long differenceDays = ChronoUnit.DAYS.between(inviteDate,nowDate);
                        // 过了锁定期 重新绑定关系
                        if(differenceDays > lockedDay){
                            MemberUserDO updateInvite = new MemberUserDO();
                            updateInvite.setId(memberUserDO.getId());
                            updateInvite.setInviterMemberId(reqDTO.getInviterMemberId());
                            updateInvite.setInviterTime(LocalDateTime.now());
                            memberUserMapper.updateById(updateInvite);
                            memberUserDO.setInviterMemberId(reqDTO.getInviterMemberId());
                        }
                    }
                }
            }
            //如果存在则更新openid
            if (StrUtil.isEmpty(memberUserDO.getOpenId())) {
                if (StrUtil.isNotEmpty(reqDTO.getOpenId())) {
                    MemberUserDO update = new MemberUserDO();
                    update.setId(memberUserDO.getId());
                    update.setOpenId(reqDTO.getOpenId());
                    memberUserMapper.updateById(update);
                    memberUserDO.setOpenId(reqDTO.getOpenId());
                }
            } else if (StrUtil.isEmpty(memberUserDO.getWxLiteOpenId())) {
                MemberUserDO update = new MemberUserDO();
                update.setId(memberUserDO.getId());
                update.setWxLiteOpenId(reqDTO.getWxLiteOpenId());
                memberUserMapper.updateById(update);
                memberUserDO.setWxLiteOpenId(reqDTO.getWxLiteOpenId());
            }
            memberUserRespDTO = UserConvert.INSTANCE.convert2(memberUserDO);
            memberUserRespDTO.setIsNew(MemberNewStatusEnum.IS_OLD.getValue());
            return memberUserRespDTO;
        }
        MemberUserDO member = createMember(reqDTO);
        memberUserRespDTO = UserConvert.INSTANCE.convert2(member);
        memberUserRespDTO.setIsNew(MemberNewStatusEnum.IS_NEW.getValue());
        return memberUserRespDTO;
    }

    private MemberUserDO createMember(MemberCreateReqDTO reqDTO) {
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        // 插入用户
        MemberUserDO user = new MemberUserDO();
        user.setAvatar(reqDTO.getAvatar());
        user.setNickname(reqDTO.getNickname());
        // 默认开启
        user.setStatus(CommonStatusEnum.ENABLE.getStatus());
        // 加密密码
        user.setPassword(encodePassword(password));
        user.setOpenId(reqDTO.getOpenId());
        user.setUnionId(reqDTO.getUnionId());
        user.setCreator("");
        user.setUpdater("");
        user.setMobile("");
        user.setRegisterIp("");
        user.setLoginIp("");
        if (ObjectUtil.isNotEmpty(reqDTO.getInviterMemberId())) {
            // 邀请时间
            user.setInviterTime(LocalDateTime.now());
            user.setInviterMemberId(reqDTO.getInviterMemberId());
        }
        user.setLoginDate(LocalDateTime.now());
        user.setTenantId(reqDTO.getTenantId());
        user.setWxLiteOpenId(reqDTO.getWxLiteOpenId());
        memberUserMapper.insert(user);
        return user;
    }

    private MemberUserDO createUser(String mobile, String registerIp) {
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        // 插入用户
        MemberUserDO user = new MemberUserDO();
        user.setMobile(mobile);
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword(password)); // 加密密码
        user.setRegisterIp(registerIp);
        memberUserMapper.insert(user);
        return user;
    }

    @Override
    public void updateUserLogin(Long id, String loginIp) {
        memberUserMapper.updateById(new MemberUserDO().setId(id)
                .setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
    }

    @Override
    public MemberUserDO getUser(Long id) {
        return memberUserMapper.selectById(id);
    }

    @Override
    public List<MemberUserDO> getUserList(Collection<Long> ids) {
        return memberUserMapper.selectBatchIds(ids);
    }

    @Override
    public void updateUserNickname(Long userId, String nickname) {
        MemberUserDO user = this.checkUserExists(userId);
        // 仅当新昵称不等于旧昵称时进行修改
        if (nickname.equals(user.getNickname())) {
            return;
        }
        MemberUserDO userDO = new MemberUserDO();
        userDO.setId(user.getId());
        userDO.setNickname(nickname);
        memberUserMapper.updateById(userDO);
    }

    @Override
    public String updateUserAvatar(Long userId, InputStream avatarFile) throws Exception {
        this.checkUserExists(userId);
        // 创建文件
        String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));
        // 更新头像路径
        memberUserMapper.updateById(MemberUserDO.builder().id(userId).avatar(avatar).build());
        return avatar;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO) {
        // 检测用户是否存在
        checkUserExists(userId);
        // TODO 芋艿：oldMobile 应该不用传递

        // 校验旧手机和旧验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getOldMobile()).setCode(reqVO.getOldCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
        // 使用新验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));

        // 更新用户手机
        memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    @Override
    public List<MemberUserDO> getInvitedMemberList(Long userId, String keyword) {
        LambdaQueryWrapper<MemberUserDO> queryWrapper = new LambdaQueryWrapperX<MemberUserDO>()
                .eq(MemberUserDO::getInviterMemberId, userId);
        if (keyword != null && !keyword.isEmpty()) {
            queryWrapper.like(MemberUserDO::getNickname, keyword);
        }

        return memberUserMapper.selectList(queryWrapper);
    }

    @Override
    public Integer getInvitedMemberCount(Long userId) {
        return Math.toIntExact(memberUserMapper.selectCount(new LambdaQueryWrapperX<MemberUserDO>()
                .eq(MemberUserDO::getInviterMemberId, userId)));
    }

    @Override
    public PageResult<AdminMemberRespVO> getMemberPageList(AdminMemberPageReqVO reqVO) {
        PageResult<MemberUserDO> memberUserPageResult = memberUserMapper.selectPage(reqVO);
        PageResult<AdminMemberRespVO> pageResult = UserConvert.INSTANCE.convertAdminMemberPage(memberUserPageResult);
        List<AdminMemberRespVO> list = pageResult.getList();
        if (CollUtil.isNotEmpty(list)) {
            for (AdminMemberRespVO vo : list) {
                if (ObjectUtil.isNotNull(vo.getInviterMemberId())) {
                    MemberUserDO memberUserDO = memberUserMapper.selectById(vo.getInviterMemberId());
                    vo.setInviterNickname(memberUserDO.getNickname());
                    vo.setInviterAvatar(memberUserDO.getAvatar());
                }
            }
        }
        return pageResult;
    }

    @Override
    public PageResult<AdminMemberRespVO> getMemberUserPage(AdminMemberPageReqVO reqVO){

        Page<AdminMemberPageReqVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<AdminMemberRespVO> memberUserPageList = memberUserMapper.getMemberUserPageList(page, reqVO);
        return new PageResult<>(memberUserPageList.getRecords(), memberUserPageList.getTotal());

    }

    @Override
    public AdminMemberDetailRespVO getMemberDetailById(Long memberId) {
        MemberUserDO memberUserDO = memberUserMapper.selectById(memberId);
        AdminMemberDetailRespVO adminMemberDetailRespVO
                = UserConvert.INSTANCE.convertAdminMemberDetailRespVO(memberUserDO);
        MemberUserDO inviterMember = memberUserMapper.selectById(memberUserDO.getInviterMemberId());
        AdminMemberDetailInviterMemberRespVO adminMemberDetailInviterMemberRespVO
                = UserConvert.INSTANCE.convertAdminMemberDetailInviterMemberRespVO(inviterMember);
        adminMemberDetailRespVO.setInviterMember(adminMemberDetailInviterMemberRespVO);
        return adminMemberDetailRespVO;
    }

    @Override
    public Boolean updateMember(AppUpdateMemberReqVO reqVO) {
        MemberUserDO memberUserDO = UserConvert.INSTANCE.convertMemberUserDO(reqVO);
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        memberUserDO.setId(loginUserId);
        memberUserMapper.updateById(memberUserDO);
        return Boolean.TRUE;
    }

    @Override
    public MemberUserDO getMemberByOpenId(String openId) {
        return memberUserMapper.selectOne(new LambdaQueryWrapperX<MemberUserDO>()
                .eq(MemberUserDO::getOpenId, openId));
    }


    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    @VisibleForTesting
    public MemberUserDO checkUserExists(Long id) {
        if (id == null) {
            return null;
        }
        MemberUserDO user = memberUserMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

}
